from datetime import datetime
import json
import time

from fastapi import Depends, Request
from fastapi_pagination.bases import AbstractPage
from fastapi_pagination.ext.databases import paginate
from sqlalchemy import insert, select, update

from forward.api.admin.config import AdminConfig
from forward.api.admin.schemas.system import (
    SystemAuthAdminCreateInSchema,
    SystemAuthAdminEditInSchema,
    SystemAuthAdminSchema,
    SystemAuthAdminUpdateInSchema,
    SystemAuthAdminListInSchema,
    SystemAuthAdminOutSchema,
    SystemAuthAdminSelfOneOutSchema,
    SystemAuthAdminSelfOutSchema,
    SystemAuthAdminDetailOutSchema,
    SystemAuthDeptOutSchema,
    SystemAuthMenuSchema,
    SystemAuthPostOutSchema,
    SystemAuthRoleOutSchema,
    SystemAuthRoleSchema,
)
from forward.dependencies.database import db
from forward.exceptions.base import AppException
from forward.http_base import HttpResp
from forward.model.tables import (
    SystemAuthAdminModel,
    SystemAuthDeptModel,
    SystemAuthRoleModel,
    SystemAuthMenuModel,
)
from forward.model.tables.system import SystemAuthPostModel
from forward.utils.redis import RedisUtil
from forward.utils.tools import ToolsUtil
from forward.utils.urls import UrlUtil
from .auth_perm import SystemAuthPermService
from .auth_role import SystemAuthRoleService


class SystemAuthAdminService:
    """系统管理员服务实现类"""

    async def find_by_username(self, username: str):
        """根据账号查找管理员"""
        row = await db.fetch_one(
            select(SystemAuthAdminModel).where(
                SystemAuthAdminModel.username == username
            )
        )
        return SystemAuthAdminSchema.model_validate(row, from_attributes=True)

    async def self(self, admin_id: int) -> SystemAuthAdminSelfOutSchema:
        """当前管理员"""
        # 管理员信息
        sys_admin_record = await db.fetch_one(
            select(SystemAuthAdminModel).where(
                SystemAuthAdminModel.id == admin_id, SystemAuthAdminModel.is_delete == 0
            )
        )
        sys_admin = SystemAuthAdminSchema.model_validate(
            sys_admin_record, from_attributes=True
        )
        # 角色权限
        auths = []
        if admin_id > 1:
            menu_ids = await self.auth_perm_service.select_menu_ids_by_role_id(
                [int(i) for i in sys_admin.role_ids.split(",")]
            )
            if menu_ids:
                menus_records = await db.fetch_all(
                    select(SystemAuthMenuModel)
                    .where(
                        SystemAuthMenuModel.id.in_(menu_ids),
                        SystemAuthMenuModel.is_disable == 0,
                        SystemAuthMenuModel.menu_type.in_(["C", "A"]),
                    )
                    .order_by(SystemAuthMenuModel.menu_sort, SystemAuthMenuModel.id)
                )
                menus = [
                    SystemAuthMenuSchema.model_validate(
                        menus_record, from_attributes=True
                    )
                    for menus_record in menus_records
                ]
                if menus:
                    auths.extend((i.perms.strip() for i in menus if i))
            if not auths:
                auths.append("")
        else:
            auths.append("*")
        admin = SystemAuthAdminSelfOneOutSchema(**sys_admin.model_dump())
        admin.avatar = await UrlUtil.to_absolute_url(admin.avatar)
        return SystemAuthAdminSelfOutSchema(user=admin, permissions=auths)

    async def list(
        self, list_in: SystemAuthAdminListInSchema
    ) -> AbstractPage[SystemAuthAdminOutSchema]:
        """管理员列表"""

        # 查询条件
        where = [SystemAuthAdminModel.is_delete == 0]
        if list_in.username:
            where.append(SystemAuthAdminModel.username.like(f"%{list_in.username}%"))
        if list_in.nickname:
            where.append(SystemAuthAdminModel.nickname.like(f"%{list_in.nickname}%"))
        if list_in.role is not None:
            # 确保 role 是一个字符串
            role_id = str(list_in.role)
            # 使用 like 来匹配包含特定角色 ID 的字符串
            where.append(SystemAuthAdminModel.role_ids.like(f"%{role_id}%"))
        query = (
            select(
                SystemAuthAdminModel,
                SystemAuthAdminModel.role_ids.label("role"),
                SystemAuthAdminModel.dept_ids.label("dept"),
                SystemAuthAdminModel.post_ids.label("post"),
            )
            .where(*where)
            .select_from(SystemAuthAdminModel)
            .order_by(SystemAuthAdminModel.id.desc(), SystemAuthAdminModel.sort.desc())
        )
        pager = await paginate(db, query)
        # 处理返回结果
        for obj in pager.lists:
            obj.avatar = await UrlUtil.to_absolute_url(obj.avatar)
            if obj.id == 1:
                obj.role = "系统管理员"
            else:
                # 角色
                role_ids = [int(i) for i in (obj.role or "").split(",") if i.isdigit()]
                roles = await db.fetch_all(
                    select(SystemAuthRoleModel).where(
                        SystemAuthRoleModel.id.in_(role_ids)
                    )
                )
                roles = [
                    SystemAuthRoleSchema.model_validate(role, from_attributes=True)
                    for role in roles
                ]
                obj.role = "/".join([i.name for i in roles])
            if not obj.dept:
                obj.dept = ""
            else:
                # 部门
                dept_ids = [int(i) for i in obj.dept.split(",") if i.isdigit()]
                depts_record = await db.fetch_all(
                    select(SystemAuthDeptModel).where(
                        SystemAuthDeptModel.id.in_(dept_ids),
                        SystemAuthDeptModel.is_delete == 0,
                    )
                )
                depts = [
                    SystemAuthDeptOutSchema.model_validate(
                        dept_record, from_attributes=True
                    )
                    for dept_record in depts_record
                ]
                obj.dept = "/".join([i.name for i in depts])
            if not obj.post:
                obj.post = ""
            else:
                # 岗位
                post_ids = [int(i) for i in obj.post.split(",") if i.isdigit()]
                post_record = await db.fetch_all(
                    select(SystemAuthPostModel).where(
                        SystemAuthPostModel.id.in_(post_ids),
                        SystemAuthPostModel.is_delete == 0,
                    )
                )
                posts = [
                    SystemAuthPostOutSchema.model_validate(
                        post_record, from_attributes=True
                    )
                    for post_record in post_record
                ]
                obj.post = "/".join([i.name for i in posts])
        return pager

    async def detail(self, id_: int) -> SystemAuthAdminDetailOutSchema:
        """管理员详细"""
        sys_admin_record = await db.fetch_one(
            select(SystemAuthAdminModel).where(
                SystemAuthAdminModel.id == id_, SystemAuthAdminModel.is_delete == 0
            )
        )
        assert sys_admin_record, "账号已不存在！"
        sys_admin_out = SystemAuthAdminDetailOutSchema.model_validate(
            sys_admin_record, from_attributes=True
        )
        sys_admin_out.avatar = await UrlUtil.to_absolute_url(sys_admin_out.avatar)
        sys_admin_out.role_ids = [
            int(i) for i in sys_admin_out.role_ids.split(",") if i.isdigit()
        ]
        sys_admin_out.dept_ids = [
            int(i) for i in sys_admin_out.dept_ids.split(",") if i.isdigit()
        ]
        sys_admin_out.post_ids = [
            int(i) for i in sys_admin_out.post_ids.split(",") if i.isdigit()
        ]
        return sys_admin_out

    async def add(self, admin_create_in: SystemAuthAdminCreateInSchema):
        """管理员新增"""
        assert not await db.fetch_one(
            select(SystemAuthAdminModel)
            .where(
                SystemAuthAdminModel.username == admin_create_in.username,
                SystemAuthAdminModel.is_delete == 0,
            )
            .limit(1)
        ), "账号已存在换一个吧！"
        assert not await db.fetch_one(
            select(SystemAuthAdminModel)
            .where(
                SystemAuthAdminModel.nickname == admin_create_in.nickname,
                SystemAuthAdminModel.is_delete == 0,
            )
            .limit(1)
        ), "昵称已存在换一个吧！"
        if not (6 <= len(admin_create_in.password) <= 20):
            raise AppException(HttpResp.FAILED, msg="密码必须在6~20位")
        create_dict = dict(admin_create_in)
        salt = ToolsUtil.random_string(5)
        create_dict["role_ids"] = ",".join([str(i) for i in admin_create_in.role_ids])
        create_dict["dept_ids"] = ",".join([str(i) for i in admin_create_in.dept_ids])
        create_dict["post_ids"] = ",".join([str(i) for i in admin_create_in.post_ids])
        create_dict["salt"] = salt
        create_dict["password"] = ToolsUtil.make_md5(
            f"{admin_create_in.password.strip()}{salt}"
        )
        create_dict["avatar"] = (
            await UrlUtil.to_relative_url(admin_create_in.avatar)
            if admin_create_in.avatar
            else "/api/static/backend_avatar.png"
        )

        await db.execute(insert(SystemAuthAdminModel).values(**create_dict))

    async def edit(self, admin_edit_in: SystemAuthAdminEditInSchema):
        """管理员编辑"""
        assert await db.fetch_one(
            select(SystemAuthAdminModel)
            .where(
                SystemAuthAdminModel.id == admin_edit_in.id,
                SystemAuthAdminModel.is_delete == 0,
            )
            .limit(1)
        ), "账号不存在了!"
        assert not await db.fetch_one(
            select(SystemAuthAdminModel)
            .where(
                SystemAuthAdminModel.username == admin_edit_in.username,
                SystemAuthAdminModel.is_delete == 0,
                SystemAuthAdminModel.id != admin_edit_in.id,
            )
            .limit(1)
        ), "账号已存在换一个吧！"
        assert not await db.fetch_one(
            select(SystemAuthAdminModel).where(
                SystemAuthAdminModel.nickname == admin_edit_in.nickname,
                SystemAuthAdminModel.is_delete == 0,
                SystemAuthAdminModel.id != admin_edit_in.id,
            )
        ), "昵称已存在换一个吧！"
        # 更新管理员信息
        admin_dict = admin_edit_in.model_dump()
        admin_dict["role_ids"] = ",".join([str(i) for i in admin_edit_in.role_ids])
        admin_dict["dept_ids"] = ",".join([str(i) for i in admin_edit_in.dept_ids])
        admin_dict["post_ids"] = ",".join([str(i) for i in admin_edit_in.post_ids])
        admin_dict["avatar"] = await UrlUtil.to_relative_url(admin_edit_in.avatar)
        if admin_edit_in.id == 1:
            del admin_dict["username"]
        if admin_edit_in.password:
            if not (6 <= len(admin_edit_in.password) <= 20):
                raise AppException(HttpResp.FAILED, msg="密码必须在6~20位")
            salt = ToolsUtil.random_string(5)
            admin_dict["salt"] = salt
            admin_dict["password"] = ToolsUtil.make_md5(
                f"{admin_edit_in.password.strip()}{salt}"
            )
        else:
            del admin_dict["password"]
        await db.execute(
            update(SystemAuthAdminModel)
            .where(SystemAuthAdminModel.id == admin_edit_in.id)
            .values(**admin_dict)
        )
        await self.cache_admin_user_by_uid(admin_edit_in.id)
        # 如果更改自己的密码,则删除旧缓存
        id_ = self.request.state.admin_id
        if admin_edit_in.password and admin_edit_in.id == id_:
            token = self.request.headers.get("token", "")
            await RedisUtil.delete(f"{AdminConfig.backstage_token_key}{token}")
            sys_admin_set_key = f"{AdminConfig.backstage_token_set}{id_}"
            ts = await RedisUtil.sget(sys_admin_set_key)
            if ts:
                await RedisUtil.delete(
                    *(f"{AdminConfig.backstage_token_key}{t}" for t in ts)
                )
            await RedisUtil.delete(sys_admin_set_key)
            await RedisUtil.sset(sys_admin_set_key, token)

    async def update(
        self, admin_update_in: SystemAuthAdminUpdateInSchema, admin_id: int
    ):
        """管理员更新"""
        sys_admin_record = await db.fetch_one(
            select(SystemAuthAdminModel).where(
                SystemAuthAdminModel.id == admin_id, SystemAuthAdminModel.is_delete == 0
            )
        )
        assert sys_admin_record, "账号不存在了!"
        sys_admin = SystemAuthAdminSchema.model_validate(
            sys_admin_record, from_attributes=True
        )
        # 更新管理员信息
        admin_dict = admin_update_in.model_dump()
        del admin_dict["curr_password"]
        admin_dict["avatar"] = (
            await UrlUtil.to_relative_url(admin_update_in.avatar)
            if admin_update_in.avatar
            else "/api/static/backend_avatar.png"
        )
        if admin_update_in.password:
            curr_pass = ToolsUtil.make_md5(
                f"{admin_update_in.curr_password}{sys_admin.salt}"
            )
            if curr_pass != sys_admin.password:
                raise AppException(HttpResp.FAILED, msg="当前密码不正确!")
            if not (6 <= len(admin_update_in.password) <= 20):
                raise AppException(HttpResp.FAILED, msg="密码必须在6~20位")
            salt = ToolsUtil.random_string(5)
            admin_dict["salt"] = salt
            admin_dict["password"] = ToolsUtil.make_md5(
                f"{admin_update_in.password.strip()}{salt}"
            )
        else:
            del admin_dict["password"]
        await db.execute(
            update(SystemAuthAdminModel)
            .where(SystemAuthAdminModel.id == sys_admin.id)
            .values(**admin_dict)
        )
        await self.cache_admin_user_by_uid(admin_id)
        # 如果更改自己的密码,则删除旧缓存
        id_ = admin_id
        if admin_update_in.password:
            token = self.request.headers.get("token", "")
            await RedisUtil.delete(f"{AdminConfig.backstage_token_key}{token}")
            sys_admin_set_key = f"{AdminConfig.backstage_token_set}{id_}"
            ts = await RedisUtil.sget(sys_admin_set_key)
            if ts:
                await RedisUtil.delete(
                    *(f"{AdminConfig.backstage_token_key}{t}" for t in ts)
                )
            await RedisUtil.delete(sys_admin_set_key)
            await RedisUtil.sset(sys_admin_set_key, token)

    async def delete(self, id_: int):
        """管理员删除"""
        assert await db.fetch_one(
            select(SystemAuthAdminModel).where(
                SystemAuthAdminModel.id == id_, SystemAuthAdminModel.is_delete == 0
            )
        ), "账号已不存在!"
        assert id_ != 1, "系统管理员不允许删除!"
        assert id_ != self.request.state.admin_id, "不能删除自己!"
        await db.execute(
            update(SystemAuthAdminModel)
            .where(SystemAuthAdminModel.id == id_)
            .values(is_delete=1, deleted_time=datetime.now())
        )
        await self.cache_admin_user_by_uid(id_)

    async def disable(self, id_: int):
        """管理员状态切换"""
        auth_admin_record = await db.fetch_one(
            select(SystemAuthAdminModel).where(
                SystemAuthAdminModel.id == id_, SystemAuthAdminModel.is_delete == 0
            )
        )
        assert auth_admin_record, "账号已不存在!"
        assert id_ != self.request.state.admin_id, "不能禁用自己!"
        auth_admin = SystemAuthAdminSchema.model_validate(
            auth_admin_record, from_attributes=True
        )
        await db.execute(
            update(SystemAuthAdminModel)
            .where(SystemAuthAdminModel.id == id_)
            .values(
                is_disable=1 if auth_admin.is_disable == 0 else 0,
            )
        )

    @classmethod
    async def cache_admin_user_by_uid(cls, id_: int):
        """缓存管理员"""
        row = await db.fetch_one(
            select(SystemAuthAdminModel).where(SystemAuthAdminModel.id == id_)
        )
        auth_admin = SystemAuthAdminSchema.model_validate(row, from_attributes=True)
        await RedisUtil.hmset(
            f"{AdminConfig.backstage_manage_key}",
            {f"{auth_admin.id}": auth_admin.model_dump_json()},
        )
        return

    def __init__(
        self,
        request: Request,
        auth_perm_service: SystemAuthPermService,
        auth_role_service: SystemAuthRoleService,
    ):
        self.request: Request = request
        self.auth_perm_service: SystemAuthPermService = auth_perm_service
        self.auth_role_service: SystemAuthRoleService = auth_role_service

    @classmethod
    async def instance(
        cls,
        request: Request,
        auth_perm_service: SystemAuthPermService = Depends(
            SystemAuthPermService.instance
        ),
        auth_role_service: SystemAuthRoleService = Depends(
            SystemAuthRoleService.instance
        ),
    ):
        """实例化"""
        return cls(request, auth_perm_service, auth_role_service)
